type Tstring_converter_info = {
    converters: Tstring_converters;
    deep: number;
    type: Tstring_convertersName;
};
type Tstring_converter = (obj: any, info: Tstring_converter_info) => string;
type Tstring_converters = {
    [Etype.undefined]?: Tstring_converter;
    [Etype.symbol]?: Tstring_converter;
    [Etype.bigint]?: Tstring_converter;
    [Etype.number]?: Tstring_converter;
    [Etype.boolean]?: Tstring_converter;
    [Etype.string]?: Tstring_converter;
    'item-string'?: Tstring_converter;
    [Etype.function]?: Tstring_converter;
    [Etype.null]?: Tstring_converter;
    [Etype.DEFAULT]?: Tstring_converter;
    ['item-DEFAULT']?: Tstring_converter;
    [Etype.NULLOBJ]?: Tstring_converter;
    [Etype.obj]?: Tstring_converter;
    [Etype.array]?: Tstring_converter;
    [Etype.HTMLElement]?: Tstring_converter;
    [Etype.KHTMLElement]?: Tstring_converter;
    [Etype.NodeList]?: Tstring_converter;
    [Etype.Map]?: Tstring_converter;
    [Etype.Set]?: Tstring_converter;
    [Etype.RegExp]?: Tstring_converter;
};
type Tstring_convertersName = 'string' // 原生String结果，有报错的需要改写。特殊值改写。
    | 'title'// 测试标题
    | 'code'// 显示原代码
    | 'code-format'// 显示原代码（格式化）
    | 'default'// 默认实现，在控制台输出时，方便调试。
    ;

interface _CU_ {
    /**
     * 转换成字符串
     * @param obj 对象
     * @param converters 转换器对象 或 装换器名
     * @returns 字符串
     */
    stringValue(obj: any, converters?: Tstring_converters | Tstring_convertersName): string;
}
{
    const get_converter = (key: string, info: Tstring_converter_info): Tstring_converter => {
        const converter = (info.converters as any)[key];
        if (converter) { return converter; }
        return U.F.fn.String;
    };
    const converter_symbol = (obj: any, info: Tstring_converter_info) => {
        return typeof obj.description === 'undefined' ? 'Symbol()' : `Symbol("${obj.description}")`;
    };
    const converter_obj = (obj: any, info: Tstring_converter_info) => {
        const result: any = [];
        info.deep = info.deep + 1;
        for (const key in obj) {
            if (Object.prototype.hasOwnProperty.call(obj, key)) {
                const value = obj[key];
                let t = U.typeof(value);
                if (t === Etype.string) { t = 'item-string' as any; }
                if (t === Etype.DEFAULT) { t = 'item-DEFAULT' as any; }
                result.push(`${key}: ${get_converter(t, info)(value, info)}`);
            }
        }
        info.deep = info.deep - 1;
        if (result.length === 0) { return '{}'; }
        if (info.type.endsWith('-format')) {
            const indent = U.S.fillStart('', info.deep * 2, ' ');
            const indentNext = U.S.fillStart('', (info.deep + 1) * 2, ' ');
            return `{\n${indentNext}${result.join(`,\n${indentNext}`)}\n${indent}}`;
        } else {
            return `{ ${result.join(', ')} }`;
        }
    };
    const converter_list = (obj: any, info: Tstring_converter_info, title: string) => {
        const result: any = [];
        obj.forEach((value: any) => {
            let t = U.typeof(value);
            if (t === Etype.string) { t = 'item-string' as any; }
            if (t === Etype.DEFAULT) { t = 'item-DEFAULT' as any; }
            result.push(get_converter(t, info)(value, info));
        });
        return `${info.type === 'title' ? title : ''}[${result.length === 0 ? '' : (' ' + result.join(', ') + ' ')}]`;
    };
    const converter_map = (obj: any, info: Tstring_converter_info) => {
        const result: any = [];
        info.deep = info.deep + 1;
        for (const [key, value] of obj) {
            let t = U.typeof(value);
            if (t === Etype.string) { t = 'item-string' as any; }
            if (t === Etype.DEFAULT) { t = 'item-DEFAULT' as any; }
            if (info.type.startsWith('code')) {
                result.push(`.set("${key}", ${get_converter(t, info)(value, info)})`);
            } else {
                result.push(`${key}:${get_converter(t, info)(value, info)}`);
            }
        }
        info.deep = info.deep - 1;
        if (result.length === 0) {
            return `${info.type === 'title' ? 'Map' : ''}{}`;
        } else {
            if (info.type === 'title') {
                return `Map{ ${result.join(', ')} }`;
            } else if (info.type.startsWith('code')) {
                return `new Map()${result.join('')}`;
            } else {
                return `{ ${result.join(', ')} }`;
            }
        }
    };
    const converter_set = (obj: any, info: Tstring_converter_info) => {
        const result: any = [];
        info.deep = info.deep + 1;
        for (const value of obj) {
            let t = U.typeof(value);
            if (t === Etype.string) { t = 'item-string' as any; }
            if (t === Etype.DEFAULT) { t = 'item-DEFAULT' as any; }
            result.push(get_converter(t, info)(value, info));
        }
        info.deep = info.deep - 1;

        if (result.length === 0) {
            return `${info.type === 'title' ? 'Set' : ''}[]`;
        } else {
            if (info.type === 'title') {
                return `Set[ ${result.join(', ')} ]`;
            } else if (info.type.startsWith('code')) {
                return `new Set([ ${result.join(', ')} ])`;
            } else {
                return `[ ${result.join(', ')} ]`;
            }
        }
    };

    const _converters_: Tstring_converters = {
        [Etype.undefined]: U.F.fn.String,
        [Etype.symbol]: converter_symbol,
        [Etype.bigint]: (obj: any, info: Tstring_converter_info) => { return `BigInt(${obj})`; },
        [Etype.number]: U.F.fn.String,
        [Etype.boolean]: U.F.fn.String,
        [Etype.string]: U.F.fn.stringValue,
        'item-string': U.F.fn.stringValue,
        [Etype.function]: U.F.fn.String,
        [Etype.null]: U.F.fn.String,
        [Etype.DEFAULT]: U.F.fn.emptyStringValue,
        'item-DEFAULT': U.F.fn.defaultValue,
        [Etype.NULLOBJ]: U.F.fn.emptyObjValue,
        [Etype.obj]: converter_obj,
        [Etype.array]: (obj: any, info: Tstring_converter_info) => {
            return converter_list(obj, info, 'Array');
        },
        [Etype.HTMLElement]: (obj: any, info: Tstring_converter_info) => { return obj.outerHTML; },
        [Etype.KHTMLElement]: (obj: any, info: Tstring_converter_info) => {
            return converter_list(obj, info, 'KHTMLElement');
        },
        [Etype.NodeList]: (obj: any, info: Tstring_converter_info) => {
            return converter_list(obj, info, 'NodeList');
        },
        [Etype.Map]: converter_map,
        [Etype.Set]: converter_set,
        [Etype.RegExp]: (obj: any, info: Tstring_converter_info) => { return obj.source; },
    };

    const _converters_string_ = Object.keys(_converters_).reduce((acc: any, key: any) => {
        switch (key) {
            case Etype.array:
            case Etype.NULLOBJ: { acc[key] = (_converters_ as any)[key]; } break;
            case Etype.DEFAULT: { acc[key] = U.F.fn.defaultValue; } break;
            case 'item-DEFAULT': { acc[key] = U.F.fn.defaultValue; } break;
            default: { acc[key] = U.F.fn.String; } break;
        }
        return acc;
    }, {});

    const _converters_title_ = U.merge(_converters_, {
        [Etype.DEFAULT]: U.F.fn.defaultValue,
        [Etype.NULLOBJ]: U.F.fn.nullObjValue,
        [Etype.HTMLElement]: (obj: HTMLElement) => { return obj.tagName; },
        [Etype.RegExp]: U.F.fn.RegExpWrap,
    });

    const _converters_code_ = U.merge(_converters_, {
        [Etype.DEFAULT]: U.F.fn.defaultValue,
        [Etype.NULLOBJ]: U.F.fn.nullObjValue,
        [Etype.RegExp]: U.F.fn.RegExpWrap,
    });

    const _converters_code_format_ = U.merge(_converters_, {
        [Etype.DEFAULT]: U.F.fn.defaultValue,
        [Etype.NULLOBJ]: U.F.fn.nullObjValue,
        [Etype.RegExp]: U.F.fn.RegExpWrap,
    });

    Object.defineProperty(_CU_.prototype, 'stringValue', {
        writable: false,
        configurable: false,
        enumerable: false,
        value: (obj: any, converters: any) => {
            let type: Tstring_convertersName = 'default';
            if (typeof converters === 'string') {
                switch (converters) {
                    case 'string': {
                        type = converters;
                        converters = _converters_string_;
                    } break;
                    case 'title': {
                        type = converters;
                        converters = _converters_title_;
                    } break;
                    case 'code': {
                        type = converters;
                        converters = _converters_code_;
                    } break;
                    case 'code-format': {
                        type = converters;
                        converters = _converters_code_format_;
                    } break;
                    default: { converters = _converters_; } break;
                }
            } else {
                converters = U.merge(_converters_, converters);
            }
            const converter_info = { converters: converters, deep: 0, type: type };
            return get_converter(U.typeof(obj), converter_info)(obj, converter_info);
        }
    });
}
